/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package idp.communication;

import idp.exceptions.BluetoothConnectionException;
import idp.exceptions.InstanceAlreadyExsistsException;
import idp.exceptions.InvalidCommandException;
import idp.exceptions.UserCanceledActionException;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Vector;
import java.util.Hashtable;
import javax.microedition.io.StreamConnection;

/**
 * Handles all incoming messages from the bluetooth device. Classes implementing
 * the {@link MessageListener} interface can register with this class to receive
 * notifications about messages. This class is a seperate thread. This class is
 * a singleton.
 * 
 * @author R. Schaafsma
 */
public class MessageController extends Thread{
	private StreamConnection _streamConnection;

	private InputStream _inputStream;

	private OutputStream _outputStream;

	private static MessageController _instance;
	
	private boolean _sendError = false;

	@Deprecated
	private Vector<String> _outputBuffer = new Vector();

	private boolean _quit = false;

	// Vector of all the messageListeners
	private static Vector<MessageListener> _listeners = new Vector<MessageListener>(
			2);

	/**
	 * return the current MessageController instance. This may be null is no
	 * instance has been created yet. This methode will NOT automatically create
	 * an instance, due to required parameters in order to do so.
	 * 
	 * @return the current MessageController instance.
	 */
	public static MessageController getInstance() {
		return _instance;
	}

	/**
	 * Tells the MessageController to quit. At most one more message will be
	 * handled before the thread stops.
	 */
	public void Quit() {
		_instance = null;
		_quit = true;
	}

	/**
	 * Constructor. Because this class is a singleton it will throw an exception
	 * if the constructor is called twice. Most classes wanting to use the
	 * MessageController should use getInstance() instead.
	 * 
	 * @param connection
	 *            an opened streamConnection to the bluetooth device
	 * @throws BluetoothConnectionException
	 *             i/o error, connection loss, etc.
	 * @throws InstanceAlreadyExsistsException
	 *             when calling this class more than once.
	 * @see #getInstance
	 */
	protected MessageController(StreamConnection connection)
			throws BluetoothConnectionException,
			InstanceAlreadyExsistsException {
		super("MessageController");
		if (_instance != null) {
			throw new InstanceAlreadyExsistsException(
					"please don't create a second one");
		}
		_streamConnection = connection;
		try {
			_inputStream = _streamConnection.openInputStream();
			_outputStream = _streamConnection.openOutputStream();
		} catch (IOException ioe) {
			throw new BluetoothConnectionException(
					"Failed to obtain input/output streams");
		}
		_instance = this;
	}

	/**
	 * Main loop for this thread.
	 */
	public void run(){
		String inputLine;
		while (!_quit) {			
			try {
				// read a single input line from the remote device and parse it.
				inputLine = ReadInputLine();
				if (inputLine != null) {
					parseMessage(inputLine);
//					System.out.println(inputLine);
				}
			} catch (IOException e) {				
				System.err.println("Connection is closed.");
				CommunicationController.getInstance().connectionError();
			} catch (InvalidCommandException e) {
				System.err.println("Received invalid command, ignoring it.");
			}
		}
		try {
			_inputStream.close();
			_outputStream.close();
			_streamConnection.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			System.err.println("error closing streams");
		}		
	}

	/**
	 * Register a {@link MessageListener}.
	 * 
	 * @param l
	 *            new listener to be registered.
	 */
	public static void addListener(MessageListener l) {
		_listeners.addElement(l);
	}

	/**
	 * Remove the specified listener.
	 * 
	 * @param l
	 *            {@link MessageListener} to be removed.
	 */
	public static void removeListener(MessageListener l) {
		_listeners.remove(l);
	}

	/**
	 * Send the passed string to the remote device. This method is not part of
	 * the thread that MessageController is running in. The message will be sent
	 * in the calling thread.
	 * 
	 * @param msg the message to be sent to the remote device
	 * @throws IOException 
	 */
	public void SendMessageToDevice(String msg) {
		char[] chars = msg.toCharArray();
		try{
		for (char b : chars) {
			_outputStream.write((int) b);
		}
		_outputStream.flush();
		System.out.println("Wrote (" + System.currentTimeMillis() + ") " + msg);
		} catch (IOException e){
			_sendError = true;
			CommunicationController.getInstance().connectionError();
		}
	}

	/**
	 * Reads a single from the input device.
	 * 
	 * @return a single line of input or null if no input is available.
	 * @throws IOException
	 */
	private String ReadInputLine() throws IOException {
		StringBuffer buffer = new StringBuffer(15);
		int data;				
	//	System.out.println(_inputStream.available());
//		while ((data = _inputStream.read()) != '\r') {
//			if ((char) data != '\n') {
//				buffer.append((char) data);
//			}
//		}	
		while(true){
			if(_inputStream.available() > 0){
				data = _inputStream.read();
				if((char) data == '\r'){
					break;
				}else if((char) data != '\n' ){
					buffer.append((char) data);
				}
			}
		}
		if (buffer.length() > 0) {
			return buffer.toString();
		} else {
			return null;
		}
	}

	/**
	 * Parses a single line of input (message) and notifies the listeners.
	 * @param msg 
	 * @throws InvalidCommandException
	 */
	public void parseMessage(String msg) throws InvalidCommandException {
		String[] command = msg.split(" ");
		if (command[0].equals("wind")) {
			for (MessageListener l : _listeners) {
				l.WindMessage(Integer.parseInt(command[1]));
			}
		} else if (command[0].equals("fzeil")) {
			for (MessageListener l : _listeners) {
				l.FokZeilMessage(Integer.parseInt(command[1]));
			}
		} else if (command[0].equals("gzeil")) {
			for (MessageListener l : _listeners) {
				l.GrootZeilMessage(Integer.parseInt(command[1]));
			}
		} else if (command[0].equals("helling")) {
			for (MessageListener l : _listeners) {
				l.HellingMessage(Integer.parseInt(command[1]));
			}
		} else if (command[0].equals("richting")) {
			for (MessageListener l : _listeners) {
				l.RichtingMessage(Integer.parseInt(command[1]));
			}
		} else if (command[0].equals("gevonden")) {
			for (MessageListener l : _listeners) {
				l.rfidTagMessage(System.currentTimeMillis(), Integer
						.parseInt(command[1]));
			}
		} else if (command[0].equals("toeren")) {
			for (MessageListener l : _listeners) {
				l.ToerenMessage(Integer.parseInt(command[1]));
			}
		} else if (command[0].equals("accu")) {
			for (MessageListener l : _listeners) {
				l.AccuMessage(Integer.parseInt(command[1]));
			}
		}
	}

}
